test(driver-sql,cli): read the dialect axis the migration-width pin cannot - #16989
Conversation
…annot The #16091 width pin runs its oracle — `SqlDriver.initObjects`, read back with `PRAGMA table_info` — on an in-memory better-sqlite3 database, while every width it asserts is a PostgreSQL claim. The step between the two is a premise nothing was holding: that the driver's character-column width does not depend on the dialect. Re-measured here: a Postgres gate as the first line of `keyableTextLength`, rebuilt into `dist` and proved present by `ablation-dist-preflight`, changes the column a real PostgreSQL deployment gets and leaves the whole pin green (28/28). Two halves now hold the premise. `sql-driver-16394-character-width-dialect-parity.test.ts` drives one object covering all three arms of `createColumn`'s character switch through the real chain on SQLite and on each live length-enforcing dialect, and asserts the columns read back from the servers' own catalogs are EQUAL rather than transcribing a width table. It lives in driver-sql because the live PG + MySQL job runs that package's suite and nothing else. A literal control on the SQLite baseline keeps parity from being satisfiable by a chain that answers `text` everywhere, and a write probe at the bound and one past it makes the reported width a statement about the column a deployment gets. The pin itself gains a server-free dialect leg: the two width bodies and the emitter's dispatch mirror are called on a driver configured for each dialect — `isPostgres` reads the config, so nothing is dialled — and must return the same value. It reddens on the mutation above rather than on the appearance of a token, so a rename cannot evade it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015QE8qk46e5CHJxyQEUjbf8
📓 Docs Drift CheckNothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 0 changed package(s)), so this run has no opinion about the docs. What this run could not see
Coarse fallback — 0 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): |
PM review — accepted, armingCI. 37 raw check-run rows, all terminal, 0 red. Four names duplicated across two workflow batches ( ⭐ The row that matters here is Clause-② — exit 0, both carriers agree, no widening tell. Red line verified by blob hash, not by reading the diff. ⭐ The hard condition was met exactlyL7b was re-driven here, not inherited — the condition triage and I both attached to this card, because the filing seat measured it once and triage explicitly recorded that it had not re-driven it either.
And the mutation was proved live in ⭐⭐ The non-redundancy measurement is the best work in this batchThe PR reports something that cuts against its own card: the repository is not wholly blind to L7b — Then it answered the objection that reading invites, with a second mutation: a gate returning a constant 64 instead of
Because the existing live coverage transcribes one width, and a gate that happens to agree with the transcription is invisible to it — which is why the new corpus carries three distinct keyed widths. That is anticipating a reviewer's objection and settling it with an experiment rather than an argument. ⭐ The placement is measured, not assertedThe live cell lives in ⛔ A correction that is mine, not this PR'sMy claim comment ( I grepped for ⭐ Sharper still: that file's comment asserts the dialect is irrelevant "confirmed by reading it: no
|
Fixes #16394
The #16091 migration-width pin runs its oracle —
SqlDriver.initObjects, readback with
PRAGMA table_info— on an in-memory better-sqlite3 database, whileevery width it asserts is a PostgreSQL claim. The step between the two is a
premise nothing was holding: the driver's character-column width does not
depend on the dialect.
L7b, re-driven here — ⛔ not inherited
The card recorded L7/L7b at head
fd79a125d1fand both the filing seat andtriage said they had not independently re-driven them. Re-run in this worktree
at
a72b6865f8, with the mutation rebuilt intodist/and proved present byscripts/ablation-dist-preflight.mjsbefore any colour was believed:dist/if (this.isPostgres) return null;as the first line ofkeyableTextLengthBoth mutate legs: blob
0e9b25dd…→03df8c12…, marker present in 2 builtfiles. Both restore legs: blob back to
0e9b25dd…, whole-treegit status --porcelainempty, and--absentreporting the marker gone fromall 6 built files. ⛔ No mutation is in the delivered diff.
premise it measured is intact; the population differs, so the counts are not
comparable and only the colour is.
a live PostgreSQL 16.13,
sql-driver-11794-richtext-text-family.test.tsreddens under it today (1 failed / 14 passed / 2 skipped). The blind
instrument is the pin, which is what the card claims — but the gap is
narrower than "nothing sees it", and the next section is the measurement that
says why the new coverage is still not redundant.
Route 2, and where each half had to live
⛔ Not route 1. A no-dialect-token source assertion reddens because a token
appeared, which a rename evades, and it returns to the layer #16298 spent four
rounds leaving.
packages/drivers/driver-sql/src/sql-driver-16394-character-width-dialect-parity.test.ts—one object covering all three arms of
createColumn's character switch, driventhrough the real chain on SQLite and on each live length-enforcing dialect, with
the columns read back from each server's own catalog through
columnInfo().The assertion is equality between the two catalogs, not a transcribed width
table: a driver change that moves every dialect together stays green here and is
caught by #16091; a change that moves ONE dialect is what this file exists to
see. A literal control on the SQLite baseline keeps parity from being satisfiable
by a chain that answers
texteverywhere, and a write probe at the bound and onepast it makes the reported width a statement about the column a deployment gets
(
22001at 65 characters, accepted at 64, on live PG 16.13).driver-sqland not in the pin because that is the only place itcan run.
Temporal Conformance (live PG + MySQL)is the sole job in thisrepository that provisions a live server, and it runs
pnpm --filter @objectstack/driver-sql testand nothing else — measured bygrepping every workflow for the URL env var, which appears in exactly one job.
A live cell written into
packages/cliwould be provisioned by no job and wouldreport itself un-run forever.
The pin itself gains a server-free dialect leg (§F2b): the two width bodies
and the emitter's dispatch mirror are called on a
DriverOracleconfigured foreach dialect the emitter branches on —
isPostgresreads the config, sonothing is dialled — and must return the same value. It reddens because the
width moved, not because a token appeared. Its control asserts the three
instances really report three different dialects, so the sweep cannot degenerate
into comparing one driver against itself.
The precedent actually used, and why not the one the dispatch named
The dispatch pointed at
schema-drift.12732-varchar-emitter-parity.test.tsas"already driven across live dialects". Read at source, it is not: it calls
dialectCell('sqlite').config()once to build an unconnected instance forvarcharColumnChars, and drives no live cell at all. Its own comment says why —"Dialect is irrelevant to that method (confirmed by reading it: no
this.dialectNamebranch)" — which is the same unenforced claim this card isabout, one file over, and which the new pin now backs with a measurement.
The shape copied instead is
sql-driver-string-maxlength-varchar.test.tsandsql-driver-11794-richtext-text-family.test.ts:declareDialectCellover[PG_CELL, MYSQL_CELL],initObjects,columnInfo(),LIVE_CELL_TIMEOUT_MSapplied at the one seam every consumer already goes through. Same testkit, same
per-file schema isolation, no third shape invented.
The non-redundancy measurement
A second gate shape,
if (this.isPostgres) return 64;— a dialect gate thatreturns a constant rather than
null— driven the same way (blob0e9b25dd…→139afad6…, marker present in 2 built files, restore proved):sql-driver-11794-richtext-text-family.test.tssql-driver-string-maxlength-varchar.test.tskeyed_text_100/keyed_markdown_255moved on PG only1 failed / 17 passed / 3 skipped. That is why the corpus carries three distinct
keyed widths: the existing live coverage transcribes one, and a gate that
happens to agree with the transcription is invisible to it.
Local verification
pnpm --filter @objectstack/cli exec vitest run --project integration src/commands/generate-string-family-width.pin.test.ts→ 30 passed (30). The pin isintegrationtier byvitest-tiers.ts's predicate (it value-imports@objectstack/driver-sql); theunittier is untouched by this diff.pnpm --filter @objectstack/driver-sql exec vitest run src/sql-driver-16394-character-width-dialect-parity.test.ts→ 1 passed / 2 skipped without servers (both live cells REPORTED, never silently omitted), 3 passed / 1 skipped against live PostgreSQL 16.13 atAsia/Shanghai(the MySQL cell is the remaining named skip — no MySQL server and no container runtime here; it is declared to the live CI job).dccac1f683:pnpm --filter @objectstack/cli test→ 227 files / 2946 tests, all passed (both tiers);pnpm --filter @objectstack/driver-sql testagainst the same live PostgreSQL → 169 files passed / 3 skipped, 3054 tests passed / 76 skipped (the skips are the unprovisioned MySQL cells, each REPORTED bydeclareUnprovisionedCell).pnpm exec eslint . --no-inline-config --format jsonatdccac1f683→ 6390 files, 0 errors, 0 warnings, exit 0. The whole population, so no narrowing is claimed.pnpm --filter @objectstack/driver-sql --filter @objectstack/cli run typecheck→ green. Both edited files are really in a tsc program:--listFilesputs the new file in driver-sql's, and the pin inpackages/cli/tsconfig.json's.scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackderives from this diff: all 53 run, all green, reconciled with--ran→ 53 derived, 53 run, 0 NOT-MEASURED, 0 UNRUN, re-derived after a freshgit fetch origin main. Two of them first returnedPREREQUISITE NOT MET(exit 3,check:dual-build-cjs-loadsandcheck:i18n-coverage) because packages outside this diff had nodist/; both were re-run to exit 0 after a fullpnpm buildrather than recorded as failures.mainmoved tob834b48e7awhile this ran; none of its 15 paths is inpackages/cliorpackages/drivers/driver-sql, so the merge-base derivation is unchanged and no scoped re-run is owed.content/delta: 0 paths, with a live positive control on both halves — the same three-dot command prints this PR's 2 non-content paths, and the-- content/pathspec really matches on a commit that touched that tree.No changeset — measured, not assumed
Both packages publish, so this is a measurement with a positive control rather
than a judgement. Built, then
npm pack --dry-run --jsonenumerated what eachpackage actually ships — 505 entries for
@objectstack/cli(including thebin/target, which npm packs regardless offiles[]) and 10 for@objectstack/driver-sql— and every packed path was searched:varcharCharsForDIALECT_ORACLESos16394_widthskeyed_markdown_255keyableTextLength(control)generateMigrationSql(control)Zero test files appear among the 515 packed entries. The controls fire, so the
zeros are readings. ⇒
skip-changeset.Contract review
Clause-②: no
Re-derived from the delivered diff, not copied from the dispatch. The diff is two
*.test.tsfiles. It relaxes no accepted set, widens no schema or publishedsurface, adds no closed-set member or registry entry, withdraws no capability and
migrates no stored data shape; the one new public method is on a test-local
subclass and is absent from every packed byte of both packages (table above).
node scripts/pm/check-widening-tells.mjs --declaration no --diff DELIVERED_DIFF_FILE→exit 0, 2 changed files read, no widening tell on any declared surface.
⛔
packages/drivers/driver-sql/src/sql-driver.tsis byte-identical tomain.Route 2 drives the driver; it does not modify it.
验收备注
Observations from this area, ⛔ not filed and not in scope for this PR:
schema-drift.12732-varchar-emitter-parity.test.tscarries a comment assertingthat
varcharColumnCharshas no dialect branch, "confirmed by reading it". Itis an unenforced claim of exactly this card's class. The new parity pin now
measures the property that comment relies on, so the claim is backed rather
than merely stated — no edit to that file is warranted for it. Successor: this
PR.
81 passed (81)figure is not reproducible at this head againstthis file, which carries 28 tests. Recorded above so a later reader does not
chase the discrepancy as a defect; nothing about the premise depends on it.
Generated by Claude Code